package org.tigris.atlas.xml.schema.mda.element;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.tigris.atlas.mda.PackageManager;
import org.tigris.atlas.mda.metadata.element.Composite;
import org.tigris.atlas.mda.metadata.element.Entity;
import org.tigris.atlas.mda.metadata.element.Field;
import org.tigris.atlas.mda.metadata.element.Query;
import org.tigris.atlas.mda.metadata.element.Reference;
import org.tigris.atlas.mda.metadata.element.Relation;

public class SchemaEntity implements Entity, NamespaceAware {

	private Entity entity;
	private Map decoratedFieldMap;
	private Map decoratedIdFieldMap;
	private Map decoratedCompositeMap;
	private Map decoratedRelationMap;
	private Map decoratedInverseRelationMap;
	private Map decoratedReferenceMap;
	private Map decoratedQueryMap;

	private Map namespaceMap;

	/**
	 * Create a new instance of <tt>Entity</tt> with the correct functionality set
	 * to generate Schema code
	 * @param entityToDecorate The <tt>Entity</tt> to decorate
	 */
	public SchemaEntity(Entity entityToDecorate) {
		if (entityToDecorate == null) {
			throw new IllegalArgumentException("SchemaEntity must be instatiated with a non-null entity!");
		}
		entity = entityToDecorate;
	}

	public String getName() {
		return entity.getName();
	}
	
	public String getDocumentation() {
		return entity.getDocumentation();
	}
	
	/**
	 * @see org.tigris.atlas.mda.metadata.element.Composite#getApplicationName()
	 */
	public String getApplicationName() {
		return entity.getApplicationName();
	}

	public String getTable() {
		return entity.getTable();
	}

	public Map getFields() {
		if (decoratedFieldMap == null) {
			Map entityFieldMap = entity.getFields();
			if ((entityFieldMap == null) || (entityFieldMap.size() == 0)) {
				decoratedFieldMap = Collections.EMPTY_MAP;

			} else {
				Field f;
				decoratedFieldMap = new HashMap((int)(entityFieldMap.size() * 1.25));
				Iterator i = entityFieldMap.values().iterator();
				while (i.hasNext()) {
					f = (Field)i.next();
					SchemaField jField = new SchemaField(f);
					decoratedFieldMap.put(f.getName(), jField);
				}

			}
		}

		return decoratedFieldMap;
	}

	public Field getField(String name) {
		return (Field)getFields().get(name);
	}

	public Map getIdFields() {
		if (decoratedIdFieldMap == null) {
			Map entityIdFieldMap = entity.getIdFields();
			if ((entityIdFieldMap == null) || (entityIdFieldMap.size() == 0)) {
				decoratedIdFieldMap = Collections.EMPTY_MAP;

			} else {
				Field f;
				decoratedIdFieldMap = new HashMap((int)(entityIdFieldMap.size() * 1.25));
				Iterator i = entityIdFieldMap.values().iterator();
				while (i.hasNext()) {
					f = (Field)i.next();
					decoratedIdFieldMap.put(f.getName(), new SchemaField(f));

				}

			}
		}

		return decoratedIdFieldMap;
	}

	public Field getIdField(String name) {
		return (Field)getIdFields().get(name);
	}

	public Map getComposites() {
		if (decoratedCompositeMap == null) {
			Map entityCompositeMap = entity.getComposites();
			if ((entityCompositeMap == null) || (entityCompositeMap.size() == 0)) {
				decoratedCompositeMap = Collections.EMPTY_MAP;

			} else {
				Composite c;
				decoratedCompositeMap = new HashMap((int)(entityCompositeMap.size() * 1.25));
				Iterator i = entityCompositeMap.values().iterator();
				while (i.hasNext()) {
					c = (Composite)i.next();
					decoratedCompositeMap.put(c.getName(), new SchemaComposite(c));

				}

			}
		}

		return decoratedCompositeMap;
	}

	public Composite getComposite(String name) {
		return (Composite)getComposites().get(name);
	}

	public Map getRelations() {
		if (decoratedRelationMap == null) {
			Map entityRelationMap = entity.getRelations();
			if ((entityRelationMap == null) || (entityRelationMap.size() == 0)) {
				decoratedRelationMap = Collections.EMPTY_MAP;

			} else {
				Relation r;
				decoratedRelationMap = new HashMap((int)(entityRelationMap.size() * 1.25));
				Iterator i = entityRelationMap.values().iterator();
				while (i.hasNext()) {
					r = (Relation)i.next();
					decoratedRelationMap.put(r.getType(), new SchemaRelation(r));

				}

			}
		}

		return decoratedRelationMap;
	}

	public Relation getRelation(String type) {
		return (Relation)getRelations().get(type);
	}

	public Map getInverseRelations() {
		if (decoratedInverseRelationMap == null) {
			Map entityInverseRelationMap = entity.getInverseRelations();
			if ((entityInverseRelationMap == null) || (entityInverseRelationMap.size() == 0)) {
				decoratedInverseRelationMap = Collections.EMPTY_MAP;

			} else {
				Entity r;
				decoratedInverseRelationMap = new HashMap((int)(entityInverseRelationMap.size() * 1.25));
				Iterator i = entityInverseRelationMap.values().iterator();
				while (i.hasNext()) {
					r = (Entity)i.next();
					decoratedInverseRelationMap.put(r.getName(), new SchemaEntity(r));

				}

			}
		}

		return decoratedInverseRelationMap;
	}

	public Relation getInverseRelation(String type) {
		return (Relation)getInverseRelations().get(type);
	}

	public Map getReferences() {
		if (decoratedReferenceMap == null) {
			Map entityReferenceMap = entity.getReferences();
			if ((entityReferenceMap == null) || (entityReferenceMap.size() == 0)) {
				decoratedReferenceMap = Collections.EMPTY_MAP;

			} else {
				Reference r;
				decoratedReferenceMap = new HashMap((int)(entityReferenceMap.size() * 1.25));
				Iterator i = entityReferenceMap.values().iterator();
				while (i.hasNext()) {
					r = (Reference)i.next();
					decoratedReferenceMap.put(r.getName(), new SchemaReference(r));

				}

			}
		}

		return decoratedReferenceMap;
	}

	public Reference getReference(String type) {
		return (Reference)getReferences().get(type);
	}

	public Map getQueries() {
		if (decoratedQueryMap == null) {
			Map entityQueryMap = entity.getQueries();
			if ((entityQueryMap == null) || (entityQueryMap.size() == 0)) {
				decoratedQueryMap = Collections.EMPTY_MAP;

			} else {
				Query q;
				decoratedQueryMap = new HashMap((int)(entityQueryMap.size() * 1.25));
				Iterator i = entityQueryMap.values().iterator();
				while (i.hasNext()) {
					q = (Query)i.next();
					decoratedQueryMap.put(q.getName(), new SchemaQuery(q));

				}

			}
		}

		return decoratedQueryMap;
	}

	public Query getQuery(String name) {
		return (Query)getQueries().get(name);
	}

	public String getLockStrategy() {
		return entity.getLockStrategy();
	}

	public boolean useOptimisticLocking() {
		return entity.useOptimisticLocking();
	}

	public String getParent() {
		return entity.getParent();
	}

	public String getSuperclass() {
		return entity.getSuperclass();
	}

	public String getUncapitalizedName() {
		return StringUtils.uncapitalize( getName() );
	}

	//////////////////////////////////////////////////////////////////////
	//schema-specific methods:
	//////////////////////////////////////////////////////////////////////

	/**
	 * Returns the schema type for this entity
	 */
	public String getSchemaType() {
		return SchemaElementUtils.getComplexTypeSchemaType(getName());
	}

	/**
	 * Return namespace name of this instance (ie, the 'xs' part of 'xmlns:xs')
	 */
	public String getNamespaceName() {
		String name = SchemaElementUtils.getNamespaceName(null, getName());
		return name;
	}

	/**
	 * Returns relative location of the namespace
	 */
	public String getNamespaceLocation() {
		String location = SchemaElementUtils.getNamespaceLocation(null, getName());
		return location;
	}
	
	/**
	 * Returns relative location of the namespace
	 */
	public String getBase() {
		String basePackage = PackageManager.getBasePackage( getProject() );
		String baseNamespace = SchemaElementUtils.translateBasePackageToBaseSchemaLocation(basePackage); 
		return baseNamespace;
	}

	public String getProject() {
		return getApplicationName();
	}

	/**
	 * Returns the set of distinct external namespace to include in the schema
	 * @return A <tt>Map</tt> of namespaces
	 */
	public Map getNamespaces() {
		if (namespaceMap == null) {
			namespaceMap = new HashMap(10);
			getEnumerationNamespaces();
			getReferenceNamespaces();
			getCompositeNamespaces();
			getRelationNamespaces();
		}

		return namespaceMap;
	}

	private void getReferenceNamespaces() {
		Map referenceMap = getReferences();
		getNamespaceEntries(referenceMap);
	}

	private void getNamespaceEntries(Map namespaceAwareMap) {
		if ((namespaceAwareMap != null) && (namespaceAwareMap.size() > 0)) {
			NamespaceAware object;
			String namespaceName;
			Iterator i = namespaceAwareMap.values().iterator();
			while (i.hasNext()) {
				object = (NamespaceAware)i.next();
				namespaceName = object.getNamespaceName();
				if (!namespaceMap.containsKey(namespaceName)) {
					SchemaNamespace sn = new SchemaNamespace(object);
					namespaceMap.put(namespaceName, sn);
				}
			}
		}
	}

	private void getCompositeNamespaces() {
		Map compositeMap = getComposites();
		getNamespaceEntries(compositeMap);
	}

	private void getRelationNamespaces() {
		Map relationMap = getRelations();
		getNamespaceEntries(relationMap);
	}

	private void getEnumerationNamespaces() {
		Map fieldMap = getFields();
		if ((fieldMap != null) && (fieldMap.size() > 0)) {
			Map enumerationMap = new HashMap(10);
			SchemaField field;
			SchemaEnumeration enumeration;
			Iterator i = fieldMap.values().iterator();
			while (i.hasNext()) {
				field = (SchemaField)i.next();
				if (field.isEnumerationType().booleanValue()) {
					enumeration = new SchemaEnumeration(field.getEnumeration());
					enumerationMap.put(enumeration.getName(), enumeration);
				}
			}

			getNamespaceEntries(enumerationMap);

		}
	}

}
